/* Example of Providing Your Own Hash Function and Equivalence Criterion */

#include <unordered_set>
#include <string>
#include <iostream>
#include <functional>

using namespace std;

template <typename T>
inline void hash_combine(std::size_t& seed, const T& val)
{
	seed ^= std::hash<T>()(val)+0x9e3779b9 + (seed << 6) + (seed >> 2);
}

template <typename T>
inline void hash_val(std::size_t &seed, const T& val)
{
	hash_combine(seed, val);
}

template <typename T, typename... Types>
inline void hash_val(std::size_t &seed, const T &val, const Types&... args)
{
	hash_combine(seed, val);
	hash_val(seed, args...);
}

template <typename... Types>
inline std::size_t hash_val(const Types&... args)
{
	std::size_t seed = 0;
	hash_val(seed, args...);
	return seed;
}


class Customer {
private:
	string fname;
	string lname;
	long no;

public:
	Customer(const string& fn, const string& ln, long n)
		: fname(fn), lname(ln), no(n) {}
	friend ostream& operator<<(ostream &strm, const Customer &c)
	{
		return strm << "[" << c.fname << "," << c.lname << "," << c.no << "]";
	}
	friend class CustomerHash;
	friend class CustomerEqual;
};

class CustomerHash
{
public:
	std::size_t operator() (const Customer &c) const
	{
		return hash_val(c.fname, c.lname, c.no);
	}
};

class CustomerEqual
{
public:
	bool operator() (const Customer &c1, const Customer &c2) const
	{
		return c1.no == c2.no;
	}
};

int hashfunc1()
{
	// unordered set with own hash function and equivalence criterion
	unordered_set<Customer, CustomerHash, CustomerEqual> custset;

	custset.insert(Customer("nico", "josuttis", 42));
	copy(custset.cbegin(), custset.cend(), ostream_iterator<Customer>(cout, "\n"));

	return 0;
}